"""
@Project ：zzzr_api_test 
@File    ：read_file.py
@IDE     ：PyCharm 
@Author  ：ZSR
@Date    ：2023/2/23 22:43 
"""
import os
from collections import Counter
from pathlib import Path
import yaml
from tool.parameter_setting import ParameterSetting
from config.config import Environment
from tool.log import logger
from tool.mysql_ import mysql_db


class ReadFile:
    # 获取当前项目目录F:\Code\api-auto-test
    project_dir = str(Path(__file__).parent.parent) + '/'

    @classmethod
    def read_yaml(cls, path):
        """读取yaml文件"""
        path = cls.project_dir + path
        file = open(path, 'r', encoding='utf-8')
        with file as doc:
            content = yaml.load(doc, Loader=yaml.Loader)
            return content

    @classmethod
    def read_case(cls):
        """读取case下需要执行的用例文件并返回用例数据"""
        # 这是多个用例文件 ['case/fastapi_test/bill.yaml', 'case/waybill.yaml']
        path_list = cls.file_execute_list()
        # 用例文件的情况 {"用例标题1":{"data":{},"path":'/ff'},"用例标题2":{"data":{},"path":'/ff'}}
        case_data = {}
        for i in path_list:
            case_data.update(cls.read_yaml(i))
        for k, v in case_data.items():
            # k为用例名称，v为用例数据
            case_name = k
            # 如果用例需要执行
            if v['is_run']:
                # 把用例标题写进最终请求数据中
                v['case_title'] = case_name
                # 前置sql执行
                if v['precondition_sql']:
                    for i in v['precondition_sql']:
                        mysql_db.execute_db(i)
                # 请求参数sql处理
                if v['data']:
                    """字典在遍历过程中不能修改字典，先把有sql的请求参数的键值分别存入两个列表，
                    然后找到有sql的值，使用mysql方法替换掉，再把2个列表组合为字典，然后更新原来的请求"""
                    sql_k_list = []
                    sql_v_list = []
                    for data_k, data_v in v['data'].items():
                        if 'sql-' in str(data_v):
                            sql_k_list.append(data_k)
                            sql_result = mysql_db.select_db(data_v[4:])
                            sql_v_list.append(sql_result)
                    new_v = dict(zip(sql_k_list, sql_v_list))
                    """在执行 update() 方法时，如果被更新的字典中已包含对应的键值对，那么原
                    value会被覆盖；如果被更新的字典中不包含对应的键值对，则该键值对被添加进去"""
                    v['data'].update(new_v)
                logger.info(f'最终执行的用例数据:{case_data}')
                yield v

    @classmethod
    def yaml_write_cookie(cls, cookie):
        # 判断需要写入cookie的环境
        if Environment == 'test':
            test_or_pro = 'test_environment'
        else:
            test_or_pro = 'pro_environment'
        environment_path = 'config/environment.yaml'
        # 读取配置文件
        environment = cls.read_yaml(environment_path)
        # 把cookie赋值给相应环境
        environment[test_or_pro]['headers']['cookie'] = cookie
        # 重新写入环境信息
        with open(cls.project_dir + environment_path, 'w', encoding="utf-8") as f:
            yaml.dump(environment, f)

    @classmethod
    def file_execute_list(cls, exclude_dir=None, exclude_file=None):
        """
        :param exclude_dir: 要排除的目录（二级目录）例子：login
        :param exclude_file: 要排除的文件（case目录下所有文件）例子：case/login/test.yaml   case/test.yaml
        :return: 获取case下的所有用例文件列表,最多支持二级目录,通用排除文件返回最终要执行的用例文件
        """
        if exclude_file is None:
            exclude_file = ['case/login/test.yaml']
        if exclude_dir is None:
            exclude_dir = []
        file_list = []
        case_path = cls.project_dir + 'case'
        # case目录下的所有文件
        for filename in os.listdir(case_path):
            if 'yaml' in filename:
                # 要储存为case开头的目录，方便读取用例使用
                file_list.append('case/' + filename)
            else:
                # 遍历case下面的二级目录
                for y in os.listdir(case_path + '/' + filename):
                    # 检查这个二级目录是否需要被排除
                    if filename in exclude_dir:
                        continue
                    # 要储存为case开头的目录，方便读取用例使用，这是二级目录得把二级目录拼接上
                    file_list.append('case/' + filename + '/' + y)
        # 文件列表不为空的话一个一个的排除掉这些文件
        if exclude_file:
            for file in exclude_file:
                file_list.remove(file)
        return file_list

    @classmethod
    def case_file_location(cls, case_title):
        '''
        :param case_title: 用例名称
        :return: 判断这个用例名称是不是在哪一个文件里面（前提用例名称唯一），返回文件名（含路径）
        '''
        path_list = cls.file_execute_list()
        for i in path_list:
            if case_title in cls.read_yaml(i).keys():
                return i

    @classmethod
    def check_case_title_is_sole(cls):
        # 先拿到执行用例的目录
        exclude_list = cls.file_execute_list()
        # 把所有要执行的用例的标题取出来
        case_title_list = []
        for i in exclude_list:
            # i 是一个用例文件，case_title_list是用例文件被转换成字典的格式的列表
            case_dict = cls.read_yaml(i)
            # 把用例写到列表里面
            case_key_list = [i for i in case_dict.keys()]
            case_title_list += case_key_list

        # 查重操作
        b = dict(Counter(case_title_list))
        repetition = {key: value for key, value in b.items() if value > 1}
        # 判断字典是否为空，空则：bool(repetition) = False
        if bool(repetition):
            logger.error(f"有重复的用例标题，请检查执行的用例文件，重复标题和重复次数为{repetition}")
            return True
        else:
            return False


if __name__ == '__main__':
    # 路径使用读取文件的相对路径
    # 读取环境配置文件测试
    # print(ReadFile.read_yaml('config/environment.yaml'))
    # 读取用例文件测试
    # case_data = ReadFile.read_yaml('case/article/test_article.yaml')
    # print(case_data)
    # # 测试用例数据生成器返回

    # case_list = ReadFile.read_case()
    # for i in case_list:
    #     print(i)

    print(ReadFile.check_case_title_is_sole())
